SQL 처리를 위한 다양한 확장 기능

바인딩과 문자열 치환을 구분하고, 조건·반복·전처리 SQL을 단계적으로 시험합니다.

QCN

dbclient 모듈의 주요 기능

  • 개발자가 직접 SQL 쿼리를 작성하고 관리할 수 있도록 합니다.
  • 복잡하거나 최적화가 필요한 쿼리, 혹은 데이터베이스 특정 기능을 활용해야 하는 경우에 큰 장점이 됩니다.
  • 조건문(if), 반복문(foreach)과 같이 동적 SQL 지원해서 다양한 조건에 따라 유연하게 쿼리를 구성합니다.

ORM(Object-Relational Mapping) 프레임워크가 자동으로 SQL을 생성하는 것과 대비되는 부분입니다.

QCN

SQL 매개변수 바인딩 (1/2)

HandStack은 데이터베이스가 제공하는 네이티브 매개변수 바인딩을 기본으로 사용합니다. 이는 SQL Injection 공격을 방어하는 가장 기본적인 방법입니다.

  • @ 접두사: 표준적인 SQL 매개변수 표기법

  • : 접두사: Oracle 등에서 사용하는 표기법

  • ? : 순서 기반 매개변수 표기법

SELECT UserID, UserName
FROM   Users
WHERE  UserID = @UserID;
QCN

SQL 매개변수 바인딩 (2/2)

  • 위 예제의 @UserID는 SQL 문법이 아닌 값으로 전달됩니다. 문자열 치환과 구분합니다.
QCN

문자열 변수 치환: ${...} 와 #{...} (1/2)

테이블명·컬럼명처럼 값 바인딩을 쓸 수 없는 식별자에만 제한적으로 사용합니다. LIKE의 검색 값은 바인딩할 수 있습니다.

${...}와 #{...}는 SQL 문자열 치환입니다. 사용자 입력을 직접 넣지 말고, 식별자는 서버의 허용 목록에서 선택합니다.

  • ${...}: 따옴표 없이 변수 값을 그대로 치환합니다.

  • #{...}: 변수 값을 문자열로 간주하여 따옴표로 감싸 치환합니다.

SELECT UserID, UserName
FROM   Users
WHERE  UserName LIKE '%' + #{KEYWORD} + '%'
QCN

문자열 변수 치환: ${...} 와 #{...} (2/2)

SELECT UserID, UserName, Email
FROM   ${TABLE_NAME}
WHERE  Status = 'Active'
QCN

로그인 세션 매개변수 사용 (1/2)

사용자의 세션 정보(예: 사용자 ID, 부서 코드)를 SQL 쿼리에서 직접 사용할 수 있습니다. 세션 정보는 로그인 과정에서 업무에 따라 부여합니다.

  • 세션 변수는 $ 접두사를 사용합니다.

  • 컨트롤러나 비즈니스 로직에서 세션 값을 매번 전달할 필요가 없어 코드가 간결해집니다.

<param id="@$CONCURRENTCOMPANYNO" type="String" length="-1" value="NULL" />
<param id="@$CONCURRENTUSERNO" type="String" length="-1" value="NULL" />
QCN

로그인 세션 매개변수 사용 (2/2)

WHERE DOF.COMPANY_NO IN (${$CONCURRENTCOMPANYNO})
  AND DOF.EMPLOYEE_NO IN (${$CONCURRENTUSERNO})
QCN

전처리 변수 추가: <pretreatment> (1/2)

SQL 실행 전에 특정 쿼리를 먼저 실행하고, 그 결과를 다음 쿼리의 매개변수로 사용할 수 있습니다.

  • resultType="Row": 단일 행 결과를 반환하여 각 컬럼을 변수로 사용합니다.

  • resultType="List": 목록 값을 반환하여 지정된 반복 변수명으로 사용합니다.

QCN

전처리 변수 추가: <pretreatment> (2/2)

<pretreatment resultType="Row">
  SELECT MinorCode AS ProcessStatus, MinorName AS ProcessStatusName
  FROM   CommonCode
  WHERE  MajorCode = 'STATUS' AND MinorCode = @StatusCode
</pretreatment>

  UPDATE Orders SET Status = @ProcessStatus, StatusName = @ProcessStatusName
  WHERE  OrderID = @OrderID
QCN

치환 변수 추가: <bind>

기존 변수들을 조합하거나 가공하여 새로운 변수를 만들어 SQL 내에서 사용할 수 있습니다.

  • 복잡한 표현식을 XML 내에서 처리하여 SQL을 더 간결하게 유지할 수 있습니다.
<bind name="SearchStart" value="StartDate + ' 00:00:00'" />
<bind name="SearchEnd" value="EndDate + ' 23:59:59'" />

  SELECT *
  FROM   EventLogs
  WHERE  EventTime BETWEEN @SearchStart AND @SearchEnd
QCN

잠깐, 구분해 보기

사용자 입력을 ${...}로 넣어도 매개변수 바인딩처럼 안전할까요?

QCN

동적 쿼리: <if> 조건문

조건에 따라 SQL 구문을 동적으로 추가하거나 변경할 수 있습니다. 검색 조건이 다양한 경우에 매우 유용합니다.

  • test 속성에 JavaScript와 유사한 조건식을 사용합니다.
  SELECT * FROM Products
  WHERE 1 = 1
  <if test="(Keyword != '')">
    AND ProductName LIKE '%' + #{Keyword} + '%'
  </if>
  <if test="(CategoryID > 0)">
    AND CategoryID = @CategoryID
  </if>
QCN

동적 쿼리: <foreach> 반복문 (1/2)

배열이나 리스트 형태의 매개변수를 처리할 때 사용합니다. IN 절을 동적으로 생성하는 데 효과적입니다.

  • collection: 반복할 대상 매개변수 (배열)

  • item: 각 반복에서의 현재 요소 변수명

  • open, close, separator: 반복문의 시작, 끝, 구분자 문자열

QCN

동적 쿼리: <foreach> 반복문 (2/2)

  SELECT *
  FROM   Users
  WHERE  UserID IN
  <foreach collection="UserIDs" item="id" open="(" close=")" separator=",">
    @id
  </foreach>
QCN

<param> 초기값 설정하기 (1/2)

매개변수에 대한 초기값을 설정할 때 특정 규칙에 따라 값을 설정할 수 있습니다.

  • <param value="1234">: KEYWORD 매개변수가 요청에 없을 경우 기본값을 설정합니다.
  • NULL 은 매개변수가 요청에 없을 경우 무시합니다.
<statement>
  <param id="@UserID" type="String" length="255" value="NULL" />
</statement>
QCN

<param> 초기값 설정하기 (2/2) · 세부 1/2

dbclient가 제공하는 키워드로 ID·시각·참/거짓·DB NULL 값을 지정합니다.

  • "@SUID": sequentialIdGenerator.NewId().ToString("N");

  • "@GUID": Guid.NewGuid();

  • "@NOW": DateTime.Now;

  • "@UTCNOW": DateTime.UtcNow;

  • "@TRUE": true;

QCN

<param> 초기값 설정하기 (2/2) · 세부 2/2

  • "@FALSE": false;

  • "@DBNULL": DBNull.Value;

<statement>
  <param id="@UserID" type="String" length="255" value="@SUID" />
</statement>
QCN

<param> 변환값 설정하기 (1/2)

매개변수에 대한 초기값을 설정하거나, 특정 규칙에 따라 값을 변환하는 30개 이상의 키워드를 제공합니다.

  • "@FOREACH": apple,banana,cherry 값을 <foreach> 에 사용

  • "@LOWER": Value.ToLower();

  • "@UPPER": Value.ToUpper();

  • "@STRING2DATETIME|format": DateTime.TryParse(format);

  • "@DATETIME2STRING|format": DateTime.ToString(format);

QCN

<param> 변환값 설정하기 (2/2)

<statement>
  <param id="@TransformRoles" type="String" length="100" value="NULL" transform="@FOREACH" />
</statement>

더 많은 변환 키워드는 dbclient/DataClient/QueryDataClient.cs 에서 TransformValue 메서드 참조

QCN

핸즈온: 쿼리 테스트 및 XML 작성 (1/2)

  • 쿼리 도구 에서 쿼리 테스트
    • SQL Server Management Studio, DBeaver 클라이언트 도구를 엽니다.
    • 업무 SQL 쿼리를 작성하고 실행하며 원하는 결과가 나오는지 테스트합니다.
    • 동적 조건이 필요한 부분은 주석으로 표기하며 여러 케이스를 테스트합니다.
QCN

핸즈온: 쿼리 테스트 및 XML 작성 (2/2)

  • HandStack XML 쿼리로 변환
    • 테스트가 완료된 SQL을 dbclient의 XML 파일로 옮깁니다.
    • 동적으로 처리했던 부분을 <if>, <foreach> 등의 태그를 사용하여 변경합니다.
    • 매개변수는 @ 표기법으로 바꾸고, 필요한 경우 <bind>, <param> 등을 추가하여 쿼리를 완성합니다.
QCN

동적 SQL의 확인 기준

  • 바인딩·치환·세션 변수의 차이를 설명합니다.
  • pretreatment·bind·if·foreach의 입력과 결과를 확인합니다.
  • 기본값·변환값은 빈 값, 경계값, 잘못된 입력으로 시험합니다.
QCN

발표: 첫 화면의 목표를 말한 뒤 핵심 개념과 예제로 진행합니다. 확인 질문 뒤에는 답할 시간을 주고, 마지막 완료 기준을 남겨 질문을 받습니다. 발표 구성 참고: MIT OpenCourseWare, Patrick Winston, How to Speak (2018), https://ocw.mit.edu/courses/res-tll-005-how-to-speak-january-iap-2018/pages/how-to-speak/

질문 후 잠시 기다립니다. 답이 없으면 앞에서 본 예제를 다시 가리킵니다. 확인할 답: 문자열 치환과 바인딩은 다릅니다. 값은 바인딩하고 식별자 치환은 허용 목록으로 제한합니다. 다음 주제로 넘어가기 전에 차이를 청중의 표현으로 한 번 확인합니다.

질문을 받는 동안 이 확인 기준을 화면에 남깁니다. 청중이 자신의 업무에 적용할 다음 행동 하나를 고르게 합니다.